package example.players;

import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import roshambo.Player;

public class IvanPlayer implements Player {

    public static final int WORSENS = 0, IMPROVEMENT = 1, INERTIA = 2;
    public static final int STG_EQUILIBRIUM = 0, STG_EQUALITY = 1, STG_CICLE = 2, STG_LOOKING_FORWARD = 3;
    public static final int MYCICLE_1 = 0, MYCICLE_2 = 1, MYCICLE_3 = 2, MYCICLE_4 = 3, MYCICLE_5 = 4, MYCICLE_6 = 5;

    private int score;
    private int lastMove;
    private int lastStrategy;
    private final int strategyScore[][];
    /**
     * Conta as partidas
     */
    private int count;
    /**
     * total de partidas
     */
    private int games;
    /**
     * tipo de movimento
     */
    private final int[] countMe;
    private final int[] countOther;
    private int[] historyMe;
    private int[] historyOther;

    public IvanPlayer() {
        countMe = new int[4];
        countOther = new int[4];
        historyMe = null;
        historyOther = null;
        strategyScore = new int[3][4];
    }

    @Override
    public void newGame(int games) {
        this.games = games;
        historyMe = new int[games];
        historyOther = new int[games];
        count = 0;
        score = 0;
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 4; j++) {
                strategyScore[i][j] = 0;
            }
        }
        countMe[PEDRA] = countMe[PAPEL] = countMe[TESOURA] = 0;
        countOther[PEDRA] = countOther[PAPEL] = countOther[TESOURA] = 0;
        lastStrategy = STG_EQUILIBRIUM;
    }

    @Override
    public int play() {
        int move = strategies();
        countMe[move]++;
        historyMe[count] = move;
        count++;
        lastMove = move;
        return move;
    }

    private int equilibrium() {
        Random rand = new Random();;
        return rand.nextInt(3) + 1;
//        if ((double) countMe[TESOURA] / count < 0.3333333333) {
//            return TESOURA;
//        } else if ((double) countMe[PEDRA] / count < 0.3333333333) {
//            return PEDRA;
//        }
//        return PAPEL;
    }

    @Override
    public void receive(int result) {
        historyOther[count - 1] = result;
        try {
            calScore(result);
        } catch (Exception ex) {
            Logger.getLogger(IvanPlayer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private int strategies() {

        int move = 0;
        int max_count = 10;
        while (move == 0) {
            switch (lastStrategy) {
                case STG_EQUILIBRIUM:
                    if (count < max_count) {
                        move = equilibrium();
                    } else if (strategyScore[IMPROVEMENT][STG_EQUALITY] >= 0) {
                        lastStrategy = STG_EQUALITY;
                    } else if (strategyScore[IMPROVEMENT][STG_CICLE] >= 0) {
                        lastStrategy = STG_CICLE;
                    } else {
                        move = equilibrium();
                    }
                    break;
                case STG_EQUALITY:
                    if (strategyScore[WORSENS][STG_EQUALITY] < 2) {
                        move = allEquals();
                        if (move == 0) {
                            lastStrategy = STG_LOOKING_FORWARD;
                            strategyScore[IMPROVEMENT][STG_EQUALITY] = -1;
                        }
                    } else {
                        lastStrategy = STG_LOOKING_FORWARD;
                    }
                    break;
                case STG_CICLE:
                    if (strategyScore[WORSENS][STG_CICLE] < 2) {
                        move = cicle();
                        if (move == 0) {
                            lastStrategy = STG_LOOKING_FORWARD;
                            strategyScore[IMPROVEMENT][STG_CICLE] = -1;
                        }
                    } else {
                        lastStrategy = STG_LOOKING_FORWARD;
                    }
                    break;
                case STG_LOOKING_FORWARD:
                    if (strategyScore[WORSENS][STG_LOOKING_FORWARD] < 3) {
                        move = lookingForward();
                        if (move == 0) {
                            lastStrategy = STG_EQUILIBRIUM;
                            strategyScore[IMPROVEMENT][STG_EQUILIBRIUM] = -1;
                        }
                    } else {
                        lastStrategy = STG_EQUILIBRIUM;
                    }
                    break;
            }
        }
        /**
         * move = allEquals(); lastStrategy = STG_EQ; if (move == 0) { if
         * (worsening < 2) { move = cicle(); lastStrategy = STG_CICLE; } } if
         * (move == 0) { System.out.println("strategy3"); move =
         * strategy3(lasts); lastStrategy = STG_EQ; } if (move == 0) {
         * System.out.println("STG_EQ"); lastStrategy = STG_EQ; move =
         * equilibrium(); }
         */
        return move;
    }

    private int allEquals() {
        // os últimos 4 todos iguais
        boolean allEq = true;
        int move = 0;
        for (int i = 1; i < 4; i++) {
            if (last(0) != last(i)) {
                allEq = false;
            }
        }
        if (allEq) {
            switch (last(0)) {
                case PEDRA:
                    move = PAPEL;
                    break;
                case PAPEL:
                    move = TESOURA;
                    break;
                case TESOURA:
                    move = PEDRA;
                    break;
            }
        }
        return move;
    }

    private int cicle() {
        // jogadas em ciclo de 4
//        System.out.println(Arrays.toString(lasts));
        int move = 0;
        switch (last(2)) {
            case PEDRA:
                if ((last(1) == PAPEL && last(0) == TESOURA)
                        || (last(0) == PAPEL && last(1) == TESOURA)) {
                    move = PAPEL;
                }
                break;
            case PAPEL:
                if ((last(1) == TESOURA && last(0) == PEDRA)
                        || (last(0) == TESOURA && last(1) == PEDRA)) {
                    move = TESOURA;
                }
                break;
            case TESOURA:
                if ((last(1) == PEDRA && last(0) == PAPEL)
                        || (last(0) == PEDRA && last(1) == PAPEL)) {
                    move = PEDRA;
                }
                break;
        }
        return move;
    }

    private void calScore(int result) throws Exception {
        int payoff = myPayoff(lastMove, result);
        score += payoff;
        if (payoff < 0) {
            strategyScore[IMPROVEMENT][lastStrategy] = 0;
            strategyScore[WORSENS][lastStrategy]++;
        } else if (payoff > 0) {
            strategyScore[IMPROVEMENT][lastStrategy]++;
            strategyScore[WORSENS][lastStrategy] = 0;
        }
    }

    private int myPayoff(int p1, int p2) throws Exception {
        if (p1 == p2) {
            return 0;
        }

        if (p1 == PEDRA) {
            if (p2 == PAPEL) {
                return -1;
            } else {
                return 1;
            }
        }

        if (p1 == PAPEL) {
            if (p2 == TESOURA) {
                return -1;
            } else {
                return 1;
            }
        }

        if (p1 == TESOURA) {
            if (p2 == PEDRA) {
                return -1;
            } else {
                return 1;
            }
        }

        throw new Exception("Only {1,2,3} values are allowed to be played. Received {" + p1 + ", " + p2 + "}");
    }

    private int last(int i) {
        if (count - i - 1 < 0) {
            System.out.println("asd");
        }
        return historyOther[count - i - 1];
    }

    private int lastMe(int i) {
        return historyMe[count - i - 1];
    }

    private int lookingForward() {
        int move = 0;

        if (lastMove == PEDRA) {
            move = TESOURA;
        } else if (lastMove == PAPEL) {
            move = PEDRA;
        } else {
            move = PAPEL;
        }
        return move;
    }

}
